package com.jy.oms.framework.asyn.task.core;

import com.alibaba.fastjson.JSON;
import com.jy.oms.framework.asyn.task.annotation.EnableTask;
import com.jy.oms.framework.asyn.task.annotation.Task;
import com.jy.oms.framework.asyn.task.annotation.TaskExclusiveStrategy;
import com.jy.oms.framework.asyn.task.annotation.TaskLimiterStrategy;
import com.jy.oms.framework.asyn.task.annotation.TaskStrategy;
import com.jy.oms.framework.asyn.task.constants.TaskConstants;
import com.jy.oms.framework.asyn.task.constants.TaskExecuteStrategyEnum;
import com.jy.oms.framework.asyn.task.exception.TaskConfigException;
import com.jy.oms.framework.asyn.task.handler.ITaskHandler;
import com.jy.oms.framework.asyn.task.handler.ITaskHandlerManager;
import com.jy.oms.framework.asyn.task.handler.ITaskStrategyInitializing;
import com.jy.oms.framework.asyn.task.handler.MethodTaskHandler;
import com.jy.oms.framework.asyn.task.model.TaskConfig;
import com.jy.oms.framework.asyn.task.model.TaskGroup;
import com.jy.oms.framework.asyn.task.model.TaskHandlerWrapper;
import com.jy.oms.framework.asyn.task.model.strategy.*;
import com.jy.oms.framework.asyn.task.util.ExecuteStrategyUtil;
import com.jy.oms.framework.util.GenericClassUtil;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * job/task任务执行器解析
 *
 * @author liuquan
 */
@Component
public class ExecutorHandlerParser implements ApplicationContextAware {
    private static Logger logger = LoggerFactory.getLogger(ExecutorHandlerParser.class);
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ExecutorHandlerParser.applicationContext = applicationContext;
    }

    /**
     * 在容器加载完毕后扫描handler
     */
    public void parse() throws Exception {
        this.scanMethodAnnotation(EnableTask.class);
        this.registClassTaskHandler();

        ITaskHandlerManager taskHandlerManager = applicationContext.getBean(ITaskHandlerManager.class);
        Map<TaskGroup, List<TaskHandlerWrapper>> taskGroupListMap = taskHandlerManager.loadAllExecutableTaskHandler();
        if (MapUtils.isEmpty(taskGroupListMap)) {
            return;
        }
        List<Map<String, String>> taskCodeList = taskGroupListMap.entrySet().stream().map(entry -> {
            Map<String, String> groupMap = new LinkedHashMap<>();
            groupMap.put("taskGroup", entry.getKey().getGroup());
            groupMap.put("taskQueue", entry.getKey().getTaskQueueType().name());
            groupMap.put("taskCodes", entry.getValue().stream().map(TaskHandlerWrapper::getTaskCode).collect(Collectors.joining(",")));
            return groupMap;
        }).collect(Collectors.toList());
        logger.info("解析Task完毕. task总览: 【{}】", JSON.toJSONString(taskCodeList));
    }

    private void scanMethodAnnotation(Class<? extends Annotation> clazz) throws Exception {
        Map<String, Object> jobBeanMap = applicationContext.getBeansWithAnnotation(clazz);
        if (jobBeanMap != null && jobBeanMap.size() > 0) {
            for (Map.Entry<String, Object> entry : jobBeanMap.entrySet()) {
                Class<?> targetClass = AopUtils.getTargetClass(entry.getValue());
                Method[] methods = targetClass.getDeclaredMethods();
                for (Method method : methods) {
                    //泛型场景下需判断
                    if (!method.isBridge()) {
                        if (clazz == EnableTask.class) {
                            this.registMethodTaskHandler(entry.getValue(), method);
                        }
                    }
                }
            }
        }
    }

    private void registMethodTaskHandler(Object bean, Method method) {
        ITaskHandlerManager taskHandlerManager = applicationContext.getBean(ITaskHandlerManager.class);
        Task task = AnnotationUtils.findAnnotation(method, Task.class);
        if (task != null) {
            String taskCode = task.taskCode();
            if (StringUtils.isBlank(taskCode)) {
                throw new TaskConfigException("task编码不能为空，error ->[" + bean.getClass() + "] .");
            }
            if (taskHandlerManager.loadTaskHandler(taskCode) != null) {
                throw new TaskConfigException("task编码重复, error ->[" + taskCode + "].");
            }
            if (task.retryNum() > TaskConstants.DEFAULT_TASK_RETRY_NUM) {
                throw new TaskConfigException("任务重试次数不能超过" + TaskConstants.DEFAULT_TASK_RETRY_NUM + "，error ->[" + bean.getClass() + "] .");
            }

            Class<?>[] parameterTypes = method.getParameterTypes();
            Class<?> returnType = method.getReturnType();
            if (parameterTypes.length != 1) {
                throw new TaskConfigException("task执行方法只能有一个参数，error ->[" + bean.getClass() + "#" + method.getName() + "].");
            }

            String callback = task.callback();
            Method callbackMethod = null;
            if (StringUtils.isNotBlank(callback)) {
                try {
                    //回调方法必须是两个参数，分别为任务执行方法的参数和返回值类型
                    callbackMethod = bean.getClass().getDeclaredMethod(callback, parameterTypes[0], returnType);
                    callbackMethod.setAccessible(true);
                } catch (NoSuchMethodException e) {
                    throw new TaskConfigException("task回调方法必须是两个参数，分别为任务执行方法[" + method.getName()
                            + "]的参数[" + parameterTypes[0].getName() + "]和返回值类型[" + returnType.getName() + "]，" +
                            "error ->[" + bean.getClass() + "#" + method.getName() + "] .");
                }
            }

            ExecuteStrategyObj executeStrategy = null;
            TaskStrategy taskStrategy = AnnotationUtils.findAnnotation(method, TaskStrategy.class);
            if (taskStrategy != null) {
                TaskLimiterStrategy taskLimiterStrategy = taskStrategy.limiterStrategy();
                TaskExclusiveStrategy taskExclusiveStrategy = taskStrategy.exclusiveStrategy();
                if(taskLimiterStrategy.enableLimiter() && taskExclusiveStrategy.enableExclusive()){
                    throw new TaskConfigException("不能同时允许限速、排他策略同时存在, taskCode: " + taskCode);
                }

                if(taskLimiterStrategy.enableLimiter()){
                    LimiterExecuteStrategy limiterExecuteStrategy = new LimiterExecuteStrategy(taskCode, TaskExecuteStrategyEnum.Limiter);
                    limiterExecuteStrategy.setAdjustable(taskLimiterStrategy.limiterAdjustable());
                    limiterExecuteStrategy.setTimeUnit(taskLimiterStrategy.limiterTimeUnit());
                    limiterExecuteStrategy.setInterval(taskLimiterStrategy.limiterInterval());
                    limiterExecuteStrategy.setLimiter(taskLimiterStrategy.limiterLimiter());
                    executeStrategy = limiterExecuteStrategy;
                }else if(taskExclusiveStrategy.enableExclusive()){
                    if(taskExclusiveStrategy.exclusiveSameBill()){
                        ExclusiveSameBillExecuteStrategy exclusiveSameBillExecuteStrategy = new ExclusiveSameBillExecuteStrategy(taskCode, TaskExecuteStrategyEnum.Group);
                        exclusiveSameBillExecuteStrategy.setTaskGroup(taskStrategy.groupTaskGroup());
                    }else{
                        ExclusiveGroupExecuteStrategy exclusiveGroupExecuteStrategy = new ExclusiveGroupExecuteStrategy(taskCode, TaskExecuteStrategyEnum.Group);
                        exclusiveGroupExecuteStrategy.setTaskGroup(taskStrategy.groupTaskGroup());
                        exclusiveGroupExecuteStrategy.setTaskExclusiveGroup(taskExclusiveStrategy.taskExclusiveGroup());
                    }
                }else{
                    GroupExecuteStrategy groupExecuteStrategy = new GroupExecuteStrategy(taskCode, TaskExecuteStrategyEnum.Group);
                    groupExecuteStrategy.setTaskGroup(taskStrategy.groupTaskGroup());
                    executeStrategy = groupExecuteStrategy;
                }

                //校验任务执行策略对象
                ExecuteStrategyUtil.checkExecuteStrategy(bean, executeStrategy);
                executeStrategy.setTaskGroupCoreThreadNum(taskStrategy.taskGroupCoreThreadNum());
                executeStrategy.setTaskGroupDbQueueName(taskStrategy.taskGroupDbQueueName());
            } else {
                executeStrategy = ExecuteStrategyUtil.buildGroupPerConsumerExecuteStrategy(taskCode);
            }

            TaskConfig taskConfig = new TaskConfig();
            taskConfig.setRetryNum(task.retryNum());
            taskConfig.setRetryContinuous(task.retryContinuous());
            taskConfig.setRetryIntervalSeconds(task.retryIntervalSeconds());
            taskConfig.setRetryBackoff(task.retryBackoff());
            taskConfig.setTaskQueue(task.taskQueue());
            taskConfig.setUpdateTaskParamWhenException(task.updateTaskParamWhenException());
            taskConfig.setKeepUniqueTaskAccordParam(task.keepUniqueTaskAccordParam());

            TaskHandlerWrapper taskHandlerWrapper = new TaskHandlerWrapper();
            taskHandlerWrapper.setTaskCode(task.taskCode());
            taskHandlerWrapper.setExecuteStrategy(executeStrategy);
            taskHandlerWrapper.setTaskHandler(new MethodTaskHandler(task.taskCode(), bean, method, callbackMethod));
            taskHandlerWrapper.setTaskHandlerParamType(parameterTypes[0]);
            taskHandlerWrapper.setTaskHandlerResultType(returnType);
            taskHandlerWrapper.setTaskQueue(task.taskQueue());
            taskHandlerWrapper.setTaskConfig(taskConfig);
            taskHandlerManager.registTaskHandler(taskCode, taskHandlerWrapper);
        }
    }

    @SuppressWarnings("rawtypes")
    private void registClassTaskHandler() {
        ITaskHandlerManager taskHandlerManager = applicationContext.getBean(ITaskHandlerManager.class);
        Map<String, ITaskHandler> beanMap = applicationContext.getBeansOfType(ITaskHandler.class);
        if (beanMap != null && beanMap.size() > 0) {
            for (Map.Entry<String, ITaskHandler> entry : beanMap.entrySet()) {
                ITaskHandler taskHandler = entry.getValue();
                Task task = AnnotationUtils.findAnnotation(taskHandler.getClass(), Task.class);
                if (task == null) {
                    throw new TaskConfigException("类上未定义@Task注解，error ->[" + taskHandler.getClass() + "] .");
                }

                String taskCode = task.taskCode();
                if (StringUtils.isBlank(taskCode)) {
                    throw new TaskConfigException("task编码不能为空，error ->[" + taskHandler.getClass() + "] .");
                }
                if (taskHandlerManager.loadTaskHandler(taskCode) != null) {
                    throw new TaskConfigException("task编码重复，error ->[" + taskHandler.getClass() + "] .");
                }
                if (task.retryNum() > TaskConstants.DEFAULT_TASK_RETRY_NUM) {
                    throw new TaskConfigException("任务重试次数不能超过" + TaskConstants.DEFAULT_TASK_RETRY_NUM + "，error ->[" + taskHandler.getClass() + "] .");
                }

                //解析任务处理类泛型参数
                ResolvableType taskHandlerParamType = GenericClassUtil.resolveGenericClassType(taskHandler, ITaskHandler.class);
                if (taskHandlerParamType == null) {
                    throw new TaskConfigException("TaskHandler不是ITaskHandler类型，error ->[" + taskHandler.getClass() + "] .");
                }
                Class<?> paramType = taskHandlerParamType.resolveGeneric(0);
                Class<?> resultType = taskHandlerParamType.resolveGeneric(1);
                if (paramType == null || resultType == null) {
                    throw new TaskConfigException("TaskHandler未定义[ITaskHandler]泛型参数，error ->[" + taskHandler.getClass() + "] .");
                }

                //解析任务执行策略参数对象
                ExecuteStrategyObj executeStrategyObj;
                if (taskHandler instanceof ITaskStrategyInitializing) {
                    ResolvableType taskStrategyParamType = GenericClassUtil.resolveGenericClassType(taskHandler, ITaskStrategyInitializing.class);
                    if (taskStrategyParamType == null) {
                        throw new TaskConfigException("TaskHandler不是TaskStrategyInitializing类型，error ->[" + taskHandler.getClass() + "] .");
                    }

                    Class<?> strategyObjType = taskStrategyParamType.resolveGeneric(0);
                    if (strategyObjType == null) {
                        throw new TaskConfigException("TaskHandler未定义[TaskStrategyInitializing]泛型参数，error ->[" + taskHandler.getClass() + "] .");
                    }
                    try {
                        Constructor<ExecuteStrategyObj> constructor = (Constructor<ExecuteStrategyObj>) strategyObjType
                                .getConstructor(String.class, TaskExecuteStrategyEnum.class);
                        executeStrategyObj = constructor.newInstance(taskCode, ExecuteStrategyUtil.getTaskExecuteStrategy(strategyObjType));
                        //自定义策略
                        ((ITaskStrategyInitializing) taskHandler).initExecuteStrategy(executeStrategyObj);
                        //校验任务执行策略对象
                        ExecuteStrategyUtil.checkExecuteStrategy(taskHandler, executeStrategyObj);
                    } catch (Exception e) {
                        throw new TaskConfigException(e);
                    }
                } else {
                    executeStrategyObj = ExecuteStrategyUtil.buildGroupPerConsumerExecuteStrategy(taskCode);
                }

                TaskConfig taskConfig = new TaskConfig();
                taskConfig.setRetryNum(task.retryNum());
                taskConfig.setRetryContinuous(task.retryContinuous());
                taskConfig.setRetryIntervalSeconds(task.retryIntervalSeconds());
                taskConfig.setRetryBackoff(task.retryBackoff());
                taskConfig.setTaskQueue(task.taskQueue());
                taskConfig.setUpdateTaskParamWhenException(task.updateTaskParamWhenException());
                taskConfig.setKeepUniqueTaskAccordParam(task.keepUniqueTaskAccordParam());

                TaskHandlerWrapper taskHandlerWrapper = new TaskHandlerWrapper();
                taskHandlerWrapper.setTaskCode(task.taskCode());
                taskHandlerWrapper.setExecuteStrategy(executeStrategyObj);
                taskHandlerWrapper.setTaskHandler(taskHandler);
                taskHandlerWrapper.setTaskHandlerParamType(paramType);
                taskHandlerWrapper.setTaskHandlerResultType(resultType);
                taskHandlerWrapper.setTaskQueue(task.taskQueue());
                taskHandlerWrapper.setTaskConfig(taskConfig);
                taskHandlerManager.registTaskHandler(taskCode, taskHandlerWrapper);
            }
        }
    }

}
